using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

using Conscript;
using Conscript.Compiler;
using Conscript.Runtime;

namespace DungeonOfDespair
{
    public class Room
        : HostFunctionHandler
    {
        private static Image s_imageRoom = Properties.Resources.Room1;
        private static bool s_bAlternateRoom = false;
      
        private static Image s_imagePortal = Properties.Resources.Portal;
        private static Image s_imagePedestal = Properties.Resources.Pedestal;

        private static Sprite[] s_spriteTorch = null;
        private static int s_iTorchFrame = 0;

        private static Image s_imageGlowRed = Properties.Resources.GlowRed;
        private static Image s_imageGlowGreen = Properties.Resources.GlowGreen;
        private static Image s_imageGlowYellow = Properties.Resources.GlowYellow;
        private static Image s_imageGlowBlue = Properties.Resources.GlowBlue;

        private static Image s_imageVialRed = Properties.Resources.VialRed;
        private static Image s_imageVialGreen = Properties.Resources.VialGreen;
        private static Image s_imageVialYellow = Properties.Resources.VialYellow;
        private static Image s_imageVialBlue = Properties.Resources.VialBlue;

        private static Font s_font
            = new Font(FontFamily.GenericSerif, 16, GraphicsUnit.Pixel);

        private static Rectangle s_rectWalls = new Rectangle(112, 116, 416, 262);
        private static Rectangle s_rectNorth = new Rectangle(296, 98, 48, 20);
        private static Rectangle s_rectSouth = new Rectangle(296, 358, 48, 20);
        private static Rectangle s_rectWest = new Rectangle(96, 214, 20, 54);
        private static Rectangle s_rectEast = new Rectangle(524, 214, 20, 54);

        private Dungeon m_dungeon;
        private ScriptManager m_scriptManager;
        private ScriptContext m_scriptContextCutscene;
        private Player m_player;
        private Boss m_boss;
        private String m_strId;
        private bool m_bPedestal;
        private Doors m_doors;
        private bool m_bTorchNW;
        private bool m_bTorchNE;
        private bool m_bTorchSW;
        private bool m_bTorchSE;
        private Enemies m_enemies;
        private Spells m_spells;
        private Effects m_effects;

        private int m_iSpeechInterval;
        private Image m_imageSpeech;
        private String m_strSpeechText;

        private bool InRectangle(Rectangle rectangle, int iX, int iY)
        {
            if (iX < rectangle.Left) return false;
            if (iX > rectangle.Right) return false;
            if (iY < rectangle.Top) return false;
            if (iY > rectangle.Bottom) return false;
            return true;
        }

        private void DoWallCollisions(Character character)
        {
            // walls
            Room roomCurrent = m_dungeon.CurrentRoom;

            if (character.X > roomCurrent.Walls.Right)
            {
                character.X = roomCurrent.Walls.Right;
                character.Move(0, 0);
            }

            if (character.X < roomCurrent.Walls.Left)
            {
                character.X = roomCurrent.Walls.Left;
                character.Move(0, 0);
            }

            if (character.Y > roomCurrent.Walls.Bottom)
            {
                character.Y = roomCurrent.Walls.Bottom;
                character.Move(0, 0);
            }

            if (character.Y < roomCurrent.Walls.Top)
            {
                character.Y = roomCurrent.Walls.Top;
                character.Move(0, 0);
            }
        }

        private void DoSpellCollisions()
        {
            for (int iSpellIndex = m_spells.Count - 1; iSpellIndex >= 0; iSpellIndex--)
            {
                Spell spell = m_spells[iSpellIndex];

                if (spell.Friendly)
                {
                    // player spells against enemies
                    for (int iEnemyIndex = m_enemies.Count - 1; iEnemyIndex >= 0; iEnemyIndex--)
                    {
                        Enemy enemy = m_enemies[iEnemyIndex];

                        if (!enemy.Active) continue;
                        if (!enemy.CollidingWith(spell)) continue;

                        enemy.Health -= spell.Damage;
                        spell.Active = false;
                        if (enemy.Health <= 0)
                        {
                            Effect effect = null;

                            effect = new Effect(m_effects, Properties.Resources.EnemyBlast, 20);
                            effect.X = enemy.X;
                            effect.Y = enemy.Y - 30;
                            m_effects.Add(effect);

                            effect = new Effect(m_effects, Properties.Resources.EnemyBlast, 15);
                            effect.X = enemy.X;
                            effect.Y = enemy.Y - 30;
                            m_effects.Add(effect);

                            enemy.Active = false;
                        }
                    }
                }
                else
                {
                    // enemy spells against player
                    if (!m_player.CollidingWith(spell)) continue;

                    m_player.Health -= spell.Damage;
                    spell.Active = false;
                }
            }
        }

        private void DoVialPickups()
        {
            if (m_dungeon.PortalRoom == this) return;
            if (m_player.X < 295) return;
            if (m_player.X > 345) return;
            if (m_player.Y < 210) return;
            if (m_player.Y > 270) return;

            if (m_dungeon.RedVialRoom == this)
            {
                m_player.HasRedVial = true;
                m_dungeon.RedVialRoom = null;
            }

            if (m_dungeon.GreenVialRoom == this)
            {
                m_player.HasGreenVial = true;
                m_dungeon.GreenVialRoom = null;
            }

            if (m_dungeon.YellowVialRoom == this)
            {
                m_player.HasYellowVial = true;
                m_dungeon.YellowVialRoom = null;
            }

            if (m_dungeon.BlueVialRoom == this)
            {
                m_player.HasBlueVial = true;
                m_dungeon.BlueVialRoom = null;
            }
        }

        private void DoVialPlacement()
        {
            if (m_dungeon.PortalRoom != this) return;

            if (m_player.HasRedVial
                && m_player.X >= 230 && m_player.X <= 290
                && m_player.Y >= 148 && m_player.Y < 208)
                m_dungeon.RedVialRoom = this;

            if (m_player.HasGreenVial
                && m_player.X >= 350 && m_player.X <= 410
                && m_player.Y >= 148 && m_player.Y < 208)
                m_dungeon.GreenVialRoom = this;

            if (m_player.HasBlueVial
                && m_player.X >= 230 && m_player.X <= 290
                && m_player.Y >= 271 && m_player.Y < 331)
                m_dungeon.BlueVialRoom = this;

            if (m_player.HasYellowVial
                && m_player.X >= 350 && m_player.X <= 410
                && m_player.Y >= 271 && m_player.Y < 331)
                m_dungeon.YellowVialRoom = this;
        }

        private void DrawVials(Graphics graphics)
        {
            if (m_dungeon.RedVialRoom == this)
            {
                int iX = m_dungeon.PortalRoom == this ? 261 : 320;
                int iY = m_dungeon.PortalRoom == this ? 178 : 240;
                graphics.DrawImage(s_imageGlowRed, iX - 114, iY - 114, 229, 229);
                graphics.DrawImage(s_imageVialRed, iX - 16, iY - 30, 32, 39);
            }

            if (m_dungeon.GreenVialRoom == this)
            {
                int iX = m_dungeon.PortalRoom == this ? 376 : 320;
                int iY = m_dungeon.PortalRoom == this ? 178 : 240;
                graphics.DrawImage(s_imageGlowGreen, iX - 114, iY - 114, 229, 229);
                graphics.DrawImage(s_imageVialGreen, iX - 16, iY - 30, 32, 39);
            }
            if (m_dungeon.BlueVialRoom == this)
            {
                int iX = m_dungeon.PortalRoom == this ? 261 : 320;
                int iY = m_dungeon.PortalRoom == this ? 301 : 240;
                graphics.DrawImage(s_imageGlowBlue, iX - 114, iY - 114, 229, 229);
                graphics.DrawImage(s_imageVialBlue, iX - 16, iY - 30, 32, 39);
            }
            if (m_dungeon.YellowVialRoom == this)
            {
                int iX = m_dungeon.PortalRoom == this ? 376 : 320;
                int iY = m_dungeon.PortalRoom == this ? 301 : 240;
                graphics.DrawImage(s_imageGlowYellow, iX - 114, iY - 114, 229, 229);
                graphics.DrawImage(s_imageVialYellow, iX - 16, iY - 30, 32, 39);
            }
        }

        public Room(Dungeon dungeon, String strId)
        {
            if (s_spriteTorch == null)
            {
                s_spriteTorch = new Sprite[8];
                s_spriteTorch[0] = new Sprite(Properties.Resources.TorchNW00);
                s_spriteTorch[1] = new Sprite(Properties.Resources.TorchNW01);
                s_spriteTorch[2] = new Sprite(Properties.Resources.TorchNW02);
                s_spriteTorch[3] = s_spriteTorch[1];
                s_spriteTorch[4] = new Sprite(Properties.Resources.TorchNE00);
                s_spriteTorch[5] = new Sprite(Properties.Resources.TorchNE01);
                s_spriteTorch[6] = new Sprite(Properties.Resources.TorchNE02);
                s_spriteTorch[7] = s_spriteTorch[5];
            }

            m_dungeon = dungeon;
            m_scriptManager = m_dungeon.ScriptManager;
            m_player = m_dungeon.Player;
            m_boss = m_dungeon.Boss;
            m_strId = strId;
            m_bPedestal = false;
            m_doors = new Doors();
            m_bTorchNW = false;
            m_bTorchNE = false;
            m_bTorchSW = false;
            m_bTorchSE = false;
            m_enemies = new Enemies(this);
            m_spells = new Spells(this);
            m_effects = new Effects(this);

            m_iSpeechInterval = 0;
            m_imageSpeech = null;
            m_strSpeechText = "";

            m_scriptContextCutscene = null;
            String strFunctionCutscene = "Cutscene_" + m_strId;
            Script scriptCutscenes = m_dungeon.CutsceneScript;
            if (scriptCutscenes.Functions.ContainsKey(strFunctionCutscene))
            {
                m_scriptContextCutscene = new ScriptContext(
                    scriptCutscenes.Functions[strFunctionCutscene]);
                m_scriptContextCutscene.Handler = this;
                m_scriptContextCutscene.InterruptOnHostfunctionCall = true;
            }
        }

        public void Load(AssociativeArray associativeArrayRoom)
        {
            // pedestal
            if (associativeArrayRoom.ContainsKey("Pedestal"))
                m_bPedestal = (bool)associativeArrayRoom["Pedestal"];

            // get "Doors" member
            AssociativeArray associativeArrayDoors
                = (AssociativeArray)associativeArrayRoom["Doors"];

            // determine exists for current room
            if (associativeArrayDoors.ContainsKey("North"))
                m_doors.North = (String)associativeArrayDoors["North"];
            if (associativeArrayDoors.ContainsKey("South"))
                m_doors.South = (String)associativeArrayDoors["South"];
            if (associativeArrayDoors.ContainsKey("West"))
                m_doors.West = (String)associativeArrayDoors["West"];
            if (associativeArrayDoors.ContainsKey("East"))
                m_doors.East = (String)associativeArrayDoors["East"];

            // torches
            if (associativeArrayRoom.ContainsKey("TorchNW"))
                m_bTorchNW = (bool) associativeArrayRoom["TorchNW"];
            if (associativeArrayRoom.ContainsKey("TorchNE"))
                m_bTorchNE = (bool)associativeArrayRoom["TorchNE"];
            if (associativeArrayRoom.ContainsKey("TorchSW"))
                m_bTorchSW = (bool)associativeArrayRoom["TorchSW"];
            if (associativeArrayRoom.ContainsKey("TorchSE"))
                m_bTorchSE = (bool)associativeArrayRoom["TorchSE"];

            // get "Enemies" member
            AssociativeArray associativeArrayEnemies
                = (AssociativeArray)associativeArrayRoom["Enemies"];

            m_enemies.Load(associativeArrayEnemies);

        }

        public void Reset()
        {
            m_enemies.Reset();
            m_spells.Reset();
            if (m_scriptContextCutscene != null)
                m_scriptContextCutscene.Reset();
            m_boss.Active = false;
        }

        public bool ExitingNorth(Player player)
        {
            if (m_doors.North == null) return false;
            if (player.Direction != EntityDirection.Up) return false;
            return InRectangle(s_rectNorth, player.X, player.Y);
        }

        public bool ExitingSouth(Player player)
        {
            if (m_doors.South == null) return false;
            if (player.Direction != EntityDirection.Down) return false;
            return InRectangle(s_rectSouth, player.X, player.Y);
        }

        public bool ExitingWest(Player player)
        {
            if (m_doors.West == null) return false;
            if (player.Direction != EntityDirection.Left) return false;
            return InRectangle(s_rectWest, player.X, player.Y);
        }

        public bool ExitingEast(Player player)
        {
            if (m_doors.East == null) return false;
            if (player.Direction != EntityDirection.Right) return false;
            return InRectangle(s_rectEast, player.X, player.Y);
        }

        public void Update()
        {
            DoWallCollisions(m_player);
            m_enemies.Update();
            foreach (Enemy enemy in m_enemies)
                DoWallCollisions(enemy);
            m_spells.Update();
            m_effects.Update();

            // spell collisions
            DoSpellCollisions();

            // vial pickup
            DoVialPickups();

            // vial placement
            DoVialPlacement();

            // cut-scene management
            if (m_scriptContextCutscene != null
                && !m_scriptContextCutscene.Terminated)
                m_scriptContextCutscene.Execute();

            // text window
            if (m_iSpeechInterval > 0)
                --m_iSpeechInterval;
        }

        public void ToggleRoomImage()
        {
            s_bAlternateRoom = !s_bAlternateRoom;
            s_imageRoom = s_bAlternateRoom
                ? Properties.Resources.Room2
                : Properties.Resources.Room1;
        }

        public void Draw(Graphics graphics)
        {
            // room
            graphics.DrawImage(s_imageRoom, 0, 0,
                s_imageRoom.Width, s_imageRoom.Height);

            // portal
            if (m_dungeon.PortalRoom == this)
                graphics.DrawImage(s_imagePortal, 223, 134,
                    s_imagePortal.Width, s_imagePortal.Height);

            // pedestal
            if (m_bPedestal)
                graphics.DrawImage(s_imagePedestal, 239, 159,
                    s_imagePedestal.Width, s_imagePedestal.Height);

            // doors
            m_doors.Draw(graphics);

            // torches
            s_iTorchFrame = (s_iTorchFrame + 1) % 16;
            int iTorchFrame = s_iTorchFrame / 4;

            if (m_bTorchNW)
            {
                Sprite spriteTorch = s_spriteTorch[iTorchFrame];
                spriteTorch.ResetShape();
                spriteTorch.X = 240 - 153;
                spriteTorch.Y = 00;
                spriteTorch.Draw(graphics);
            }

            if (m_bTorchNE)
            {
                Sprite spriteTorch = s_spriteTorch[iTorchFrame + 4];
                spriteTorch.ResetShape();
                spriteTorch.X = 400;
                spriteTorch.Y = 00;
                spriteTorch.Draw(graphics);
            }

            if (m_bTorchSW)
            {
                Sprite spriteTorch = s_spriteTorch[iTorchFrame + 4];

                spriteTorch.ResetShape();
                spriteTorch.Scale(-1.0f, -1.0f);
                spriteTorch.X = 240;
                spriteTorch.Y = 480;
                spriteTorch.Draw(graphics);
            }

            if (m_bTorchSE)
            {
                Sprite spriteTorch = s_spriteTorch[iTorchFrame];
                spriteTorch.ResetShape();
                spriteTorch.Scale(-1.0f, -1.0f);
                spriteTorch.X = 400 + 153;
                spriteTorch.Y = 480;
                spriteTorch.Draw(graphics);
            }

            // boss (before player)
            if (m_boss.Active && m_boss.Y <= m_player.Y)
                m_boss.Draw(graphics);

            // enemies before player
            m_enemies.Draw(graphics, false);

            // player
            m_player.Draw(graphics);

            // enemies before player
            m_enemies.Draw(graphics, true);

            // boss (after player)
            if (m_boss.Active && m_boss.Y > m_player.Y)
                m_boss.Draw(graphics);

            // vials
            DrawVials(graphics);

            // spells and effects
            m_spells.Draw(graphics);
            m_effects.Draw(graphics);

            // text window
            if (m_iSpeechInterval > 0)
            {
                graphics.DrawImage(Properties.Resources.TextWindow,
                    0, 368, 640, 112);
                if (m_imageSpeech != null)
                    graphics.DrawImage(m_imageSpeech, 28, 396, 56, 56);
                graphics.DrawString(m_strSpeechText, s_font, Brushes.Black, 89.0f, 413.0f);
                graphics.DrawString(m_strSpeechText, s_font, Brushes.LightCyan, 88.0f, 412.0f);
            }
        }

        public object OnHostFunctionCall(String strFunctionName, List<object> listParameters)
        {
            if (strFunctionName == "Player_SetActive")
            {
                m_player.Active = (bool)listParameters[0];
                return null;
            }
            else if (strFunctionName == "Player_SetPosition")
            {
                m_player.X = (int)listParameters[0];
                m_player.Y = (int)listParameters[1];
                m_player.Move(0, 0);
                return null;
            }
            else if (strFunctionName == "Player_MoveTo")
            {
                m_player.MoveTo(
                    (int)listParameters[0],
                    (int)listParameters[1]);
                return null;
            }
            else if (strFunctionName == "Player_Moving")
            {
                return m_player.Moving;
            }
            else if (strFunctionName == "Player_Say")
            {
                m_imageSpeech = Properties.Resources.FacePlayer;
                m_strSpeechText = (String) listParameters[0];
                m_iSpeechInterval = m_strSpeechText.Length + 10;
                return null;
            }
            else if (strFunctionName == "Player_HasAllVials")
            {
                return m_player.HasRedVial && m_player.HasGreenVial
                    && m_player.HasYellowVial && m_player.HasBlueVial;
            }
            else if (strFunctionName == "Boss_SetActive")
            {
                m_boss.Active = (bool)listParameters[0];
                return null;
            }
            else if (strFunctionName == "Boss_SetPosition")
            {
                m_boss.X = (int)listParameters[0];
                m_boss.Y = (int)listParameters[1];
                return null;
            }
            else if (strFunctionName == "Boss_MoveTo")
            {
                m_boss.MoveTo(
                    (int)listParameters[0],
                    (int)listParameters[1]);
                return null;
            }
            else if (strFunctionName == "Boss_Moving")
            {
                return m_boss.Moving;
            }
            else if (strFunctionName == "Boss_Say")
            {
                m_imageSpeech = Properties.Resources.FaceBoss;
                m_strSpeechText = (String)listParameters[0];
                m_iSpeechInterval = m_strSpeechText.Length + 10;
                return null;
            }
            else if (strFunctionName == "Boss_SpawnEnemy")
            {
                String strEnemy = (String) listParameters[0];
                EnemyType enemyType = EnemyType.Boss;
                if (strEnemy == "Troll")
                    enemyType = EnemyType.Troll;
                else if (strEnemy == "Lizard")
                    enemyType = EnemyType.Lizard;
                else if (strEnemy == "Pig")
                    enemyType = EnemyType.Pig;
                else if (strEnemy == "Wizard")
                    enemyType = EnemyType.Wizard;
                else
                    return null;

                Enemy enemy = new Enemy(m_enemies, enemyType);
                enemy.Reset();
                m_enemies.Add(enemy);
                return null;
            }
            else if (strFunctionName == "TextWindow_IsActive")
            {
                return m_iSpeechInterval > 0;
            }
            else
                return null;
        }

        public Dungeon Dungeon
        {
            get { return m_dungeon; }
        }

        public String Id
        {
            get { return m_strId; }
        }

        public Rectangle Walls
        {
            get { return s_rectWalls; }
        }

        public Doors Doors
        {
            get { return m_doors; }
        }

        public bool TorchNW
        {
            get { return m_bTorchNW; }
            set { m_bTorchNW = value; }
        }

        public bool TorchNE
        {
            get { return m_bTorchNE; }
            set { m_bTorchNE = value; }
        }

        public bool TorchSW
        {
            get { return m_bTorchSW; }
            set { m_bTorchSW = value; }
        }

        public bool TorchSE
        {
            get { return m_bTorchSE; }
            set { m_bTorchSE = value; }
        }

        public Enemies Enemies
        {
            get { return m_enemies; }
        }

        public Spells Spells
        {
            get { return m_spells; }
        }

        public Effects Effects
        {
            get { return m_effects; }
        }
    }
}
